//
// Created by xiaofoo.
//

#include "EpicExtendCmd.h"

/**
 * EpicSetModeCmd
 */
EpicSetModeCmd::EpicSetModeCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicSetModeCmd::~EpicSetModeCmd() noexcept {}

void EpicSetModeCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicSetModeCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    if (argc != 2) {
        Tcl_WrongNumArgs(interp, 1, argv, "string1");
        return TCL_ERROR;
    }

    char *arg1 = Tcl_GetString(argv[1]);

    std::string mode = arg1;
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    dsgInfo->add_solver_res(std::make_pair("mode", mode));

    return TCL_OK;
}

void EpicSetModeCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicSetDepthCmd
 */
EpicSetDepthCmd::EpicSetDepthCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicSetDepthCmd::~EpicSetDepthCmd() noexcept {}

void EpicSetDepthCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicSetDepthCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    if (argc != 2) {
        Tcl_WrongNumArgs(interp, 1, argv, "int1");
        return TCL_ERROR;
    }

    char *arg1 = Tcl_GetString(argv[1]);

    std::string depth = arg1;
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    dsgInfo->add_solver_res(std::make_pair("depth", depth));

    return TCL_OK;
}

void EpicSetDepthCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicSetWorkerCmd
 */
EpicSetWorkerCmd::EpicSetWorkerCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicSetWorkerCmd::~EpicSetWorkerCmd() noexcept {}

void EpicSetWorkerCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicSetWorkerCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    if (argc != 2) {
        Tcl_WrongNumArgs(interp, 1, argv, "int1");
        return TCL_ERROR;
    }

    char *arg1 = Tcl_GetString(argv[1]);

    std::string worker_num = arg1;
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    dsgInfo->add_solver_res(std::make_pair("worker_num", worker_num));

    return TCL_OK;
}

void EpicSetWorkerCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * chformal
 */
EpicChformalCmd::EpicChformalCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicChformalCmd::~EpicChformalCmd() noexcept {}

void EpicChformalCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicChformalCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    std::string chformal_val = "";
    for (int i = 1; i != argc; ++i) {
        const std::string str_tmp = Tcl_GetString(argv[i]);
        chformal_val += str_tmp + " ";
    }
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    dsgInfo->add_solver_res(std::make_pair("chformal_val", chformal_val));

    return TCL_OK;
}

void EpicChformalCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicSetEnginesCmd
 */
EpicSetEnginesCmd::EpicSetEnginesCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicSetEnginesCmd::~EpicSetEnginesCmd() noexcept {}

void EpicSetEnginesCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicSetEnginesCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    std::string engine = "";
    for (int i = 1; i != argc; ++i) {
        const std::string str_tmp = Tcl_GetString(argv[i]);
        engine += str_tmp + " ";
    }
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    dsgInfo->add_engine(engine);

    return TCL_OK;
}

void EpicSetEnginesCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicReadFileCmd
 */
EpicReadFileCmd::EpicReadFileCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicReadFileCmd::~EpicReadFileCmd() noexcept {}

void EpicReadFileCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

std::vector<std::string> EpicReadFileCmd::split_string_by_space(std::string src) {
    src += " ";
    std::vector<std::string> rt;
    std::string split_str;
    for (int i = 0; i < src.length(); ++i) {
        char currentCh = src[i];
        if (currentCh != ' ') {
            split_str += currentCh;
        } else {
            if (!split_str.empty()) {
                rt.emplace_back(split_str);
                split_str = "";
            }
        }
    }
    return std::move(rt);
}

bool EpicReadFileCmd::get_macro_value(std::vector<std::string> &macro_value) {
    if (macro_value.size() != 1 && macro_value.size() != 3) {
        return false;
    }
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    if (macro_value.size() == 1) {
        dsgInfo->add_macro(std::make_pair(macro_value[0], ""));
    } else {
        dsgInfo->add_macro(std::make_pair(macro_value[0], macro_value[2]));
    }
    std::vector<std::string>().swap(macro_value);
    return true;
}

int EpicReadFileCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    int filelist_index = argc - 1;
    int top_index = argc - 1;
    int cmopts_index = -1;
    std::vector<int> reserve_index;
    std::string str_tmp;
    bool pre_is_option = false;
    for (int i = 1; i != argc; ++i) {
        str_tmp = Tcl_GetString(argv[i]);
        if (str_tmp == "-f") {
            filelist_index = i;
            pre_is_option = true;
        } else if (str_tmp == "-cmopts") {
            cmopts_index = i;
            pre_is_option = true;
        } else if (str_tmp == "-top") {
            top_index = i;
            pre_is_option = true;
        } else {
            if (!pre_is_option) {
                reserve_index.push_back(i);
            }
            pre_is_option = false;
        }
    }
    if (top_index == argc - 1 || cmopts_index == argc - 1) {
        Tcl_WrongNumArgs(interp, 1, argv, "-cmopts [macro] -top [top_module] ");
        return TCL_ERROR;
    }
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    if (filelist_index != argc - 1) {
        dsgInfo->set_filelist(Tcl_GetString(argv[filelist_index + 1]));
    }
    std::string top_module = Tcl_GetString(argv[top_index + 1]);
    dsgInfo->add_solver_res(std::make_pair("top_module", top_module));
    for (auto index : reserve_index) {
        str_tmp = Tcl_GetString(argv[index]);
        dsgInfo->add_veri_files(str_tmp);
    }
    if (cmopts_index > 0) {
        std::string cmopts_param = Tcl_GetString(argv[cmopts_index + 1]);
        std::vector<std::string> split_result = split_string_by_space(cmopts_param);
        if (split_result.at(0) != "+define") {
            return TCL_ERROR;
        }
        std::vector<std::string> macro_value;
        for (int i = 1; i < split_result.size(); ++i) {
            str_tmp = split_result.at(i);
            if (str_tmp == "+define") {
                if (!get_macro_value(macro_value)) {
                    return TCL_ERROR;
                }
            } else {
                std::string combine_str = "";
                for (int j = 0; j < str_tmp.length(); ++j) {
                    if (str_tmp[j] == '=') {
                        if ((combine_str.empty() && macro_value.size() == 0) || macro_value.size() >= 2) {
                            return TCL_ERROR;
                        }
                        if (!combine_str.empty()) {
                            macro_value.push_back(combine_str);
                            combine_str = "";
                        }
                        macro_value.push_back("=");
                    } else {
                        combine_str += str_tmp[j];
                    }
                }
                if (!combine_str.empty()) {
                    macro_value.push_back(combine_str);
                }
                if (macro_value.size() > 3) {
                    return TCL_ERROR;
                }
            }
        }
        if (!get_macro_value(macro_value)) {
            return TCL_ERROR;
        }
    }
    return TCL_OK;
}

void EpicReadFileCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicCreateClockCmd
 */
EpicCreateClockCmd::EpicCreateClockCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicCreateClockCmd::~EpicCreateClockCmd() noexcept {}

void EpicCreateClockCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicCreateClockCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    if (argc != 3) {
        return TCL_ERROR;
    }
    std::string str_tmp = Tcl_GetString(argv[1]);
    if (str_tmp != "-clock") {
        return TCL_ERROR;
    }
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    dsgInfo->set_clock_name(Tcl_GetString(argv[2]));
    return TCL_OK;
}

void EpicCreateClockCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicCreateClockCmd
 */
EpicCreateRstCmd::EpicCreateRstCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicCreateRstCmd::~EpicCreateRstCmd() noexcept {}

void EpicCreateRstCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicCreateRstCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    if (argc != 3) {
        return TCL_ERROR;
    }
    std::string str_tmp = Tcl_GetString(argv[1]);
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    if (str_tmp == "-rst") {
        dsgInfo->set_reset_polarity(true);
    } else if (str_tmp == "-rstn") {
        dsgInfo->set_reset_polarity(false);
    } else {
        return TCL_ERROR;
    }
    dsgInfo->set_reset_name(Tcl_GetString(argv[2]));
    return TCL_OK;
}

void EpicCreateRstCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicRunSimCmd
 */
EpicRunSimCmd::EpicRunSimCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicRunSimCmd::~EpicRunSimCmd() noexcept {}

void EpicRunSimCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicRunSimCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    DsgInfo *dsgInfo = DsgInfo::getInstance();
    dsgInfo->set_sim_run(true);
    int n_index = argc - 1;
    int rst_len_index = argc - 1;
    std::string str_tmp;
    for (int i = 1; i != argc; ++i) {
        str_tmp = Tcl_GetString(argv[i]);
        if (str_tmp == "-n") {
            n_index = i;
        } else if (str_tmp == "-rstlen") {
            rst_len_index = i;
        }
    }
    if (n_index != argc - 1) {
        dsgInfo->set_cycle_simulate_num(Tcl_GetString(argv[n_index + 1]));
    }
    if (rst_len_index != argc - 1) {
        dsgInfo->set_cycle_reset_num(Tcl_GetString(argv[rst_len_index + 1]));
    }
    return TCL_OK;
}

void EpicRunSimCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicCheckFvCmd
 */
EpicCheckFvCmd::EpicCheckFvCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicCheckFvCmd::~EpicCheckFvCmd() noexcept {}

void EpicCheckFvCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicCheckFvCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    std::string Epicx_home = getenv("EPICFV_HOME");
    if (Epicx_home.empty()) {
        return TCL_ERROR;
    }
    std::string tools_dir = Epicx_home + "/binary";
    std::string mpirun = tools_dir + "/share/openmpi/bin/orterun";  //"mpirun";

    DsgInfo *dsgInfo = DsgInfo::getInstance();
    std::unordered_map<std::string, std::string> res_map = dsgInfo->get_solver_res_map();
    std::vector<std::string> engine_arr = dsgInfo->get_engines();
    const std::string filelist = dsgInfo->get_files();
    std::unordered_map<std::string, std::string> marco_map = dsgInfo->get_macro_map();
    std::vector<std::string> verilog_file_arr = dsgInfo->get_veri_files();
    bool is_sim_run = dsgInfo->get_sim_run();
    std::string clock_name;
    std::string reset_name;
    std::string cycle_reset_num;
    std::string cycle_simulate_num;
    bool reset_polarity = true;
    dsgInfo->get_sim_param(clock_name, reset_name, cycle_reset_num, cycle_simulate_num, reset_polarity);

    const std::string worker_num_str = res_map["worker_num"];
    int pn = 5;
    if (!worker_num_str.empty()) {
        pn = std::stoi(worker_num_str) + 1;
    }

    std::vector<std::string> script_arr;
    std::vector<std::string> file_arr;

    json_t j;
    j["res_map"] = res_map;
    j["script_arr"] = script_arr;
    j["engine_arr"] = engine_arr;
    j["file_arr"] = file_arr;
    j["filelist"] = filelist;
    j["marco_map"] = marco_map;
    j["verilog_file_arr"] = verilog_file_arr;
    j["is_sim_run"] = is_sim_run;
    j["clock_name"] = clock_name;
    j["reset_name"] = reset_name;
    j["cycle_reset_num"] = cycle_reset_num;
    j["cycle_simulate_num"] = cycle_simulate_num;
    j["reset_polarity"] = reset_polarity;

    const std::string json_str = j.dump();

    std::ofstream fout(".tcl.json");
    if (fout) {
        fout << json_str;
        fout.close();
        fout.clear();
    }

    std::string cmdline = mpirun + " --oversubscribe -x LD_LIBRARY_PATH -np " + std::to_string(pn) + " " + tools_dir +
                          "/epic_fv_bin -f -b Epic.tcl";
    system(cmdline.c_str());

    struct stat fileStat;
    if ((stat(".tcl.json", &fileStat) == 0) && S_ISREG(fileStat.st_mode) && !getenv("EPICFV_INT_DEBUG")) {
        system("rm .tcl.json");
    }

    return TCL_OK;
}

void EpicCheckFvCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicExitCmd
 */
EpicExitCmd::EpicExitCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicExitCmd::~EpicExitCmd() noexcept {}

void EpicExitCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicExitCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    return TCL_OK;
}

void EpicExitCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

/**
 * EpicQuitCmd
 */
EpicQuitCmd::EpicQuitCmd(const char *cmdName) : EpicCommonCmd(cmdName) {}

EpicQuitCmd::~EpicQuitCmd() noexcept {}

void EpicQuitCmd::preExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}

int EpicQuitCmd::exec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {
    return TCL_OK;
}

void EpicQuitCmd::postExec(ClientData clientData, Tcl_Interp *interp, int argc, Tcl_Obj *const *argv) {}
